package com.tanhua.sso.service;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tanhua.common.exception.BusinessException;
import com.tanhua.common.mapper.UserFreezeMapper;
import com.tanhua.common.mapper.UserInfoMapper;
import com.tanhua.common.mapper.UserMapper;
import com.tanhua.common.pojo.User;
import com.tanhua.common.vo.ErrorResult;
import com.tanhua.dubbo.server.api.HuanXinApi;
import com.tanhua.dubbo.server.api.ManagerApi;
import com.tanhua.dubbo.server.api.UserLocationApi;
import com.tanhua.dubbo.server.vo.UserLocationVo;
import com.tanhua.sms.SmsTemplate;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 *
 * @ClassName: UserService
 * @Author:王晨晗
 * @Description: 用户模块业务层
 * @Date: 2021/12/25/19:03
 */
@Service
@Slf4j
public class UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserInfoMapper userInfoMapper;
    @Autowired
    private RedisTemplate<String , String> redisTemplate;
    @Autowired
    private SmsTemplate smsTemplate;    //短信模板

    @Value("${jwt.secret}")
    private String secret;      //盐值

    @Reference
    private HuanXinApi huanXinApi;

    @Reference
    private ManagerApi managerApi;

    @Autowired
    private UserFreezeMapper userFreezeMapper;

    @Reference
    private UserLocationApi userLocationApi;

    /**
     * @description:  发送验证码 ,完成验证码的生成 发送 , 并存入redis中
     * @param phone : 手机号
     * @updateTime 2021/12/29 9:13
     */
    public void sendCode(String phone) {
        // 1 查看是否冻结
        LambdaQueryWrapper<User> laWrapper=new LambdaQueryWrapper<>();
        laWrapper.eq(User::getMobile,phone);
        User userFree = userMapper.selectOne(laWrapper);
        //2 查询是否有冻结
        String freezeKey="USER_FREEZE_"+userFree.getId();
        String data = redisTemplate.opsForValue().get(freezeKey);
        boolean flag=true;
        //有冻结
        if(ObjectUtil.isNotEmpty(data)){
            // 2.1判断是否是登陆冻结
            if(StringUtils.equals(data,"1")){
                throw new BusinessException(ErrorResult.LoginFreezeFail());
            }
            flag=false;
        }
        //如果不为空没有登陆状态 还原状态
        if(flag){
            String status="1";
            managerApi.updateUserStatus(status, Convert.toInt(userFree.getId()));
        }

        //设置redis的key
        String redisKey = "CHECK_CODE_" + phone;
        //判断key在redis中是否存在
        if (redisTemplate.hasKey(redisKey)){
            throw new BusinessException(ErrorResult.redisKeyError());
        }
        //随机生成code
        //String code = smsTemplate.sendSms(phone);
        String code = "123456";         //开发环境使用固定编码

        //判断验证码发送是否成功
        if (code == null){
            throw new BusinessException(ErrorResult.fail());
        }
        //将验证码存入redis中 ,并设置有效期(5分钟)
        redisTemplate.opsForValue().set(redisKey , code , Duration.ofMinutes(5));
    }

    /**
     * @description  校验验证码
     * @param phone :手机号
     * @param code : 用户填写的验证码
     * @updateTime 2021/12/29 9:15
     * @return: java.util.Map<java.lang.String,java.lang.Object>
     */
    public Map<String, Object> checkCode(String phone, String code) {

        /*LambdaQueryWrapper<UserFreeze> wrapper=new LambdaQueryWrapper<>();
        userFreezeMapper.selectOne(wrapper);*/

        try {
            //根据手机号获取user信息
            LambdaQueryWrapper<User> wrapper1=new LambdaQueryWrapper<>();
            wrapper1.eq(User::getMobile,phone);
            User user1 = userMapper.selectOne(wrapper1);
            if(ObjectUtil.isNotEmpty(user1)){
                //找到user信息

            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        boolean isNew = false;      //是否是新用户
        String redisKey="CHECK_CODE_" + phone;      //redis中key值
        //1.获取redis中的验证码
        String redisCode = redisTemplate.opsForValue().get(redisKey);
        //2.与前台验证码进行比较验证
        if(!code.equals(redisCode)){
            //比较失败 返回客户结果
            throw new BusinessException(ErrorResult.checkCodeError());
        }
        //通过校验 查询数据库 ,判断是否是新用户
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getMobile , phone);
        User user = userMapper.selectOne(wrapper);
        //判断user是否为空
        if (user == null){
            //新用户 , 将信息存入数据库中
            user = new User();
            user.setMobile(phone);
            user.setPassword(DigestUtils.md5Hex("123456"));  //密码进行加密处理
            userMapper.insert(user);
            isNew = true;       //修改用户状态

            //注册环信用户
            Boolean result = huanXinApi.register(user.getId());
            if (!result) {
                //注册环信失败，记录日志
                log.error("注册环信用户失败~ userId = " + user.getId());
            }
        }
        //校验完成, 清除rediskey的code值
        redisTemplate.delete(redisKey);

        //token的头部信息
        Map<String, Object> header = new HashMap<String, Object>();
        header.put(JwsHeader.TYPE, JwsHeader.JWT_TYPE);
        header.put(JwsHeader.ALGORITHM, "HS256");

        //令牌中payload的信息
        Map<String ,Object> claims = new HashMap<>();
        claims.put("mobile" , phone);
        claims.put("id" , user.getId());

        //生成token
        String token = Jwts.builder()
                .setHeader(header)          //可省略
                .setClaims(claims)          //payload 存放数据
                .signWith(SignatureAlgorithm.HS256 , secret)   //加密算法 加盐
                .setExpiration(new DateTime().plusDays(20).toDate())   //设置过期时间
                .compact();
        //保存响应数据
        Map<String ,Object> map = new HashMap<>();
        map.put("token" , token);  //token
        map.put("isNew" , isNew);  //是否是新用户
        //获取用户地理位置
        UserLocationVo userLocationVo = userLocationApi.queryByUserId1(user.getId());
        //修复最近登录地点
        try {
            managerApi.updateBaseUserLastActiveTime(user.getId(),userLocationVo.getAddress());
        } catch (Exception e) {
            e.printStackTrace();
        }

        //打印日志信息, 保存到数据库;
        log.info("用户登录" , user.getId() , "user" , "华为" , userLocationVo.getAddress());
        //返回map集合
        return map;
    }

    /**
     * @description 解析token的方法
     * @param token
     * @updateTime 2021/12/29 9:22
     * @return: com.tanhua.common.pojo.User
     */
    public User queryToken(String token){
        try {
            //解析token,获取body内容
            Map<String , Object> body = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
            //将body中的内容封装到user对象中
            ObjectMapper objectMapper = new ObjectMapper();    //json对象
            String json = objectMapper.writeValueAsString(body);  //json序列化对象
            User user = JSON.parseObject(json, User.class);     //解析json成user对象
            return user;
        } catch (Exception e) {
            throw new BusinessException(ErrorResult.tokenError());
        }
    }

    /**
     * @description 修改用户手机号
     * @param id        用户id
     * @param newPhone  新手机号
     * @return
     */
    public void updatePhone(Long id, String newPhone) {
        //判断新手机号是否存在
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        //构建查询条件
        wrapper.eq("mobile" , newPhone);
        //执行查询
        User user = userMapper.selectOne(wrapper);
        //判断user是否为空
        if (user != null){
            //说明手机号已经被注册 抛出异常
            throw new BusinessException(ErrorResult.mobileError());    //手机号已注册
        }
        //未注册则进行修改
        user = new User();
        user.setId(id);     //用户id
        user.setMobile(newPhone);   //手机号
        //执行修改
        int update = userMapper.updateById(user);
        //判断修改结果
        if (update == 0){
            throw new BusinessException(ErrorResult.savePhoneError());   //手机号修改失败
        }
    }
}
